//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#include <engine.h>

UInt32 GrafixObjectStack::m_windowId = 1;
extern Boolean g_needFlipAll;

// implement at animation.cpp
//
extern Boolean AnimationLayerIsEmpty(
    /* [in] */ AnimationLayer* header);

ECode TimeWork(
        /* [in] */ Void* pThis);
ECode TimeHandler(
        /* [in] */ GrafixObjectStack* stack);

#ifndef GUI_AUTO_TEST

#if defined(_x86) && defined(_GNUC) && defined(_zener)
ECode ParseKeyboardEvent(
        /* [in] */ Void* pThis);

ECode ParseMouseEvent(
        /* [in] */ Void* pThis);

#else
ECode ParseTouchEvent(
        /* [in] */ Void* pThis);

ECode ParseKeypadEvent(
        /* [in] */ Void* pThis);
#endif

#else

Void AutoTestRelease();

ECode InitAutoTest();

ECode AutoTestParseTouchEvent(
    /* [in] */ Void* pThis);

ECode AutoTestParseKeypadEvent(
    /* [in] */ Void* pThis);

ECode AutoTestRandEvent(
    /* [in] */ Void* pThis);
#endif

ECode Transform(
        /* [in] */ Void* pThis);


ECode GrafixObjectStack::Initialize(
    /* [in] */ CGrafixEngine* engine,
    /* [in] */ Int32 width,
    /* [in] */ Int32 height)
{
    ECode ec;
    Int32 i;

    assert(engine);

    m_engine = engine;

    m_enableInput = TRUE;
    m_haveInput = FALSE;
    m_grafixObjectStackChanged = FALSE;
    m_terminate = FALSE;
    m_cursor = NULL;

    m_primarySurface = NULL;
    m_transitionSurface = NULL;
    m_backgroundSurface = NULL;

    m_pointerWindow = NULL;
    m_enteredWindow = NULL;
    m_keyboardWindow = NULL;
    m_focusedWindow = NULL;

    m_timeEvent = NULL;
    m_timeThread  = NULL;
    m_keyThread = NULL;
    m_cursorThread = NULL;

    m_cursorDriver = NULL;
    m_keyDriver = NULL;

    m_orientation = ScreenOrientation_Up;
    m_width = width;
    m_height = height;

    FusionVectorInit(&m_windows, 64);

    for (i = 0; i < KEY_CACHE; i++) {
        m_keys[i].keyValue = -1;
        m_keys[i].owner = NULL;
    }

    FusionVectorInit(&m_timeWindows, 8);
    FusionVectorInit(&m_grabbedKeys, 8);
    FusionVectorInit(&m_interceptedKeys, 8);

    m_layerChanged = FALSE;
    m_animationThread = NULL;

    m_nNumberOfAnimes = 0;
    m_pAvailableSlot = 0;
    m_bActiveFlag = FALSE;

    for (i = 0; i< MaxNumAnime; i++) {
        m_pAnime[i] = 0;
    }

    ec = DzCreateEventObj(FALSE, UNSIGNALED, &m_invokeAnimation);
    if (GFX_FAILED(ec)) {
        return ec;
    }

    // create blent surface
    //
    GrafixSurfaceParams  surParams = {
        GrafixAttribute_None,
        {0, 0, m_width, m_height},
        {-1, -1, -1, -1},
        PixelFormat_RGB24,
    };

#if defined(_wince) || defined(_win32)
    surParams.pixelFormat = PixelFormat_ARGB;
#endif

    ec = m_engine->CreateGrafixSurface(surParams, (IGrafixSurface **)&m_primarySurface);
    if (GFX_FAILED(ec)) {
        return ec;
    }

#ifdef _neptune
        DzFreeDiscontinuousKernelPages(
            (Void *)m_primarySurface->m_cacheBuffer->m_buffer.info.address);

        m_primarySurface->m_cacheBuffer->m_buffer.info.address =
            m_engine->m_currentLayer->m_info.systemAddress[0];
#endif

    memset((Void *)m_primarySurface->m_cacheBuffer->m_buffer.info.address, 0,
        m_primarySurface->m_cacheBuffer->m_buffer.size);

#ifdef _wince
    surParams.pixelFormat = PixelFormat_RGB24;
#endif

    ec = m_engine->CreateGrafixSurface(surParams, (IGrafixSurface **)&m_backgroundSurface);
    if (GFX_FAILED(ec)) {
        return ec;
    }

#ifdef _neptune
    SetColorARGB32(
        m_backgroundSurface->m_cacheBuffer->m_buffer.info.address,
        0,
        0,
        m_backgroundSurface->m_cacheBuffer->m_buffer.info.width,
        m_backgroundSurface->m_cacheBuffer->m_buffer.info.height,
        m_backgroundSurface->m_cacheBuffer->m_buffer.info.pitch,
        (Color)0xFF000000,
        0xFF);
#else
    memset((Void *)m_backgroundSurface->m_cacheBuffer->m_buffer.info.address, 0,
        m_backgroundSurface->m_cacheBuffer->m_buffer.size);
#endif

    ec = m_engine->CreateGrafixSurface(surParams, (IGrafixSurface **)&m_transitionSurface);
    if (GFX_FAILED(ec)) {
        return ec;
    }

#ifdef _neptune
        DzFreeDiscontinuousKernelPages(
            (Void *)m_transitionSurface->m_cacheBuffer->m_buffer.info.address);

        m_transitionSurface->m_cacheBuffer->m_buffer.info.address =
            (Address)Nu_DmaBufAlloc(
                    m_transitionSurface->m_cacheBuffer->m_buffer.size, FALSE);
#endif

    memset((Void *)m_transitionSurface->m_cacheBuffer->m_buffer.info.address, 0,
        m_transitionSurface->m_cacheBuffer->m_buffer.size);

    // create cursor window
    //
    GrafixObjectParams params = {
        GrafixAttribute(GrafixAttribute_Ghost
                        | GrafixAttribute_NoFocus),
        {0, 0, GhostWidth, GhostHeight},
    };
    ec = m_engine->CreateGrafixObject(L"cursor", params, (IGrafixObject **)&m_cursor);
    if (GFX_FAILED(ec)) {
        return ec;
    }

    m_timeThread = CreateSystemThread(TimeWork, (Void *)this,
                            g_pDefaultScheduleClass, g_uDefaultSchedulePolicy);
    if (!m_timeThread) {
        GFX_ERROR_MESSAGE("Create time thread failed\n");
        m_timeThread->Release();
    }

#ifdef CURSOR_VISIBLE
    IGrafixSurface* cursorSurface;
    int color = CURSOR_COLOR;

    ec = m_cursor->SetVisible(TRUE);
    if (GFX_FAILED(ec)) {
        return ec;
    }

    ec = m_cursor->GetGrafixSurface(&cursorSurface);
    if (GFX_FAILED(ec)) {
        return ec;
    }

    cursorSurface->Clear(color);
    cursorSurface->Release();

    m_cursor->m_needPaint = FALSE;
    m_cursor->m_paintStatus = PaintStatus_Completed;
#endif

#ifndef GUI_AUTO_TEST

#if defined(_x86) && defined(_GNUC) && defined(_zener)
    ec = DzCreateKernelThread(ParseMouseEvent, (Void *)this, 0,
            ThreadPriorityRank_Normal, &m_cursorThread);
    if (GFX_FAILED(ec)) {
        GFX_ERROR_MESSAGE("Create touch thread failed\n");
    }

    m_keyThread = CreateSystemThread(ParseKeyboardEvent, (Void*)this,
                            g_pDefaultScheduleClass, g_uDefaultSchedulePolicy);
    if (!m_keyThread) {
        GFX_ERROR_MESSAGE("Create keyboard thread failed\n");
    }
#else
    ec = DzCreateKernelThread(ParseTouchEvent, (Void *)this, 0,
            ThreadPriorityRank_Normal, &m_cursorThread);
    if (GFX_FAILED(ec)) {
        GFX_ERROR_MESSAGE("Create touch thread failed\n");
    }

    m_keyThread = CreateSystemThread(ParseKeypadEvent, (Void *)this,
                            g_pDefaultScheduleClass, g_uDefaultSchedulePolicy);
    if (!m_keyThread) {
        GFX_ERROR_MESSAGE("Create keypad thread failed\n");
    }
#endif

#else
    ec = InitAutoTest();
    if (GFX_FAILED(ec)) {
        return ec;
    }

    ec = DzCreateKernelThread(AutoTestParseTouchEvent, (Void *)this, 0,
            ThreadPriorityRank_Normal, &m_cursorThread);
    if (GFX_FAILED(ec)) {
        GFX_ERROR_MESSAGE("Create touch thread failed\n");
    }

    m_keyThread = CreateSystemThread(AutoTestParseKeypadEvent, (Void *)this,
        g_pDefaultScheduleClass, g_uDefaultSchedulePolicy);
    if (!m_keyThread) {
        GFX_ERROR_MESSAGE("Create keypad thread failed\n");
    }

    ec = DzCreateKernelThread(AutoTestRandEvent, (Void *)this, 0,
            ThreadPriorityRank_Normal, &m_randEvent);
    if (GFX_FAILED(ec)) {
        GFX_ERROR_MESSAGE("Create rand thread failed\n");
    }

#endif // GUI_AUTO_TEST

    ec = DzCreateKernelThread(Transform, (Void *)this, 0,
            ThreadPriorityRank_Normal, &m_animationThread);
    if (GFX_FAILED(ec)) {
        GFX_ERROR_MESSAGE("Create Transform thread failed\n");
    }

    return NOERROR;
}

GrafixObjectStack::~GrafixObjectStack()
{
    g_lock.Lock();

    m_terminate = TRUE;

    // some thread may be bring the event, delete it first;
    //
    IDeviceDriver* notifyCenter = NULL;

    ECode ec;

#ifdef _neptune
    ec = E_FAIL;
#else
    ec = DzFindService(L"device:nc0",(IInterface **)&notifyCenter);
#endif
    if (GFX_FAILED(ec)) {
        if (m_keyThread) {
            do {
                ec = m_keyThread->Interrupt();
#if defined(_zener)
                DzYield();
#elif defined(_win32)
                CThread::Sleep(50, NULL);
#elif defined(_wince)
                AURA_SLEEP(50);
#else
# add the code of the new platform
#endif
            } while (E_THREAD_ALREADY_FINISHED != ec);
        }

        if (m_cursorThread) {
            do {
                ec = m_cursorThread->Interrupt();
#if defined(_zener)
                DzYield();
#elif defined(_win32)
                CThread::Sleep(50, NULL);
#elif defined(_wince)
                AURA_SLEEP(50);
#else
# add the code of the new platform
#endif
            } while (E_THREAD_ALREADY_FINISHED != ec);
        }

    }
    else {
        KPDData inputData = {VK_PHONE_HANG_UP, 1UL << 31};
        TouchData touchData = {1, 0, 0};

        MemoryBuf keyBuf((Byte *)&inputData, sizeof(KPDData), sizeof(KPDData));
        ec = notifyCenter->Control(
            1,  // EVENT_KEYPAD
            keyBuf,
            NULL,
            NULL);
        if (GFX_FAILED(ec)) {
            do {
                ec = m_keyThread->Interrupt();
#if defined(_zener)
                DzYield();
#elif defined(_win32)
                CThread::Sleep(50, NULL);
#elif defined(_wince)
                AURA_SLEEP(50);
#else
# add the code of the new platform
#endif
            } while (E_THREAD_ALREADY_FINISHED != ec);
        }

        MemoryBuf cursorBuf((Byte *)&touchData,
                            sizeof(TouchData),
                            sizeof(TouchData));
        ec = notifyCenter->Control(
            2, // EVENT_TOUCH
            cursorBuf,
            NULL,
            NULL);
        if (GFX_FAILED(ec)) {
            do {
                ec = m_cursorThread->Interrupt();
#if defined(_zener)
                DzYield();
#elif defined(_win32)
                CThread::Sleep(50, NULL);
#elif defined(_wince)
                AURA_SLEEP(50);
#else
# add the code of the new platform
#endif
            } while (E_THREAD_ALREADY_FINISHED != ec);
        }

        notifyCenter->Release();
    }

    if (m_keyThread) {
        m_keyThread->Join(INFINITE, NULL);
        m_keyThread->Release();
        m_keyThread = NULL;
    }

    if (m_cursorThread) {
        m_cursorThread->Join(INFINITE, NULL);
        m_cursorThread->Release();
        m_cursorThread = NULL;
    }

    if (m_keyDriver) {
        m_keyDriver->Release();
        m_keyDriver = NULL;
    }

    if (m_cursorDriver) {
        m_cursorDriver->Release();
        m_cursorDriver = NULL;
    }

    if (m_timeThread) {
        do {
            ec = m_timeThread->Interrupt();
#if defined(_zener)
                DzYield();
#elif defined(_win32)
                CThread::Sleep(50, NULL);
#elif defined(_wince)
                AURA_SLEEP(50);
#else
# add the code of the new platform
#endif
        } while (E_THREAD_ALREADY_FINISHED != ec);

        m_timeThread->Release();
        m_timeThread = NULL;
    }

    if (m_animationThread) {
        do {
            ec = m_animationThread->Interrupt();
#if defined(_zener)
                DzYield();
#elif defined(_win32)
                CThread::Sleep(50, NULL);
#elif defined(_wince)
                AURA_SLEEP(50);
#else
# add the code of the new platform
#endif
        } while (E_THREAD_ALREADY_FINISHED != ec);

        m_animationThread->Release();
        m_animationThread = NULL;
    }

#ifdef GUI_AUTO_TEST
    if (m_randEvent) {
        do {
            ec = m_randEvent->Interrupt();
#if defined(_zener)
                DzYield();
#elif defined(_win32)
                CThread::Sleep(50, NULL);
#elif defined(_wince)
                AURA_SLEEP(50);
#else
# add the code of the new platform
#endif
        } while (E_THREAD_ALREADY_FINISHED != ec);

        m_randEvent->Release();
        m_randEvent = NULL;
    }

    AutoTestRelease();
#endif

    if (m_timeEvent) {
        m_timeEvent->Release();
        m_timeEvent = NULL;
    }

    Int32 index;
    Void* p;

    // close all window
    //
    fusion_vector_foreach(p, index, m_windows) {
        ((CGrafixObject *)p)->Close();
    }

    if (m_cursor) {
        m_cursor->Release();
        m_cursor = NULL;
    }

    FusionVectorDestroy(&m_windows);

    // free all grabbed key
    //
    fusion_vector_foreach(p, index, m_grabbedKeys)
    {
        free(p);
    }
    FusionVectorDestroy(&m_grabbedKeys);

    // free all intercepted key
    //
    fusion_vector_foreach(p, index, m_interceptedKeys)
    {
        free(p);
    }
    FusionVectorDestroy(&m_interceptedKeys);

    FusionVectorDestroy(&m_timeWindows);


    if (m_primarySurface) {
#ifdef _neptune
        m_primarySurface->m_cacheBuffer->m_buffer.info.address = NULL;
#endif
        m_primarySurface->Release();
        m_primarySurface = NULL;
    }

    if (m_transitionSurface) {
#ifdef _neptune
        Nu_DmaBufFree(
            m_transitionSurface->m_cacheBuffer->m_buffer.info.address,
            m_transitionSurface->m_cacheBuffer->m_buffer.size);

        m_transitionSurface->m_cacheBuffer->m_buffer.info.address = NULL;
#endif
        m_transitionSurface->Release();
        m_transitionSurface = NULL;
    }

    if (m_backgroundSurface) {
        m_backgroundSurface->Release();
        m_backgroundSurface = NULL;
    }

    if (m_invokeAnimation) {
        m_invokeAnimation->Release();
        m_invokeAnimation = NULL;
    }

    m_engine = NULL;

    GFX_EXPRESSION(DEBUG_DESTRUCTOR, kprintf("~GrafixObjectStack()\n"));

    g_lock.Unlock();
}

//
// All the applicaiton in a process use the same event buffer.
//
ECode GrafixObjectStack::AcquireEventBuffer(
    /* [in] */Int32 contextID,
    /* [out] */ CGrafixEventBuffer** eventBuffer)
{
    assert(m_engine);

    ECode ec = NOERROR;

    assert(eventBuffer);
    *eventBuffer = NULL;

    g_lock.Lock();

    CGrafixEventBuffer* head = &m_engine->m_eventBufferHeader;
    CGrafixEventBuffer* p;
    for (p = (CGrafixEventBuffer *)head->Next(); p != head;
        p = (CGrafixEventBuffer *)p->Next()) {
        if (p->m_contextId == contextID) {
            p->AddRef();
            *eventBuffer = p;

            goto Exit;
        }
    }

    *eventBuffer = new CGrafixEventBuffer;
    if (!(*eventBuffer)) {
        GFX_ERROR_MESSAGE("Not enough memory\n");
        ec = E_OUT_OF_MEMORY;

        goto Exit;
    }

    (*eventBuffer)->AddRef();
    ec = (*eventBuffer)->constructor(contextID, this);
    if (GFX_FAILED(ec)) {
        (*eventBuffer)->Release();
        *eventBuffer = NULL;
    }

    // the eventbuffer link doesn't hold the eventbuffer's reference.
    //
    m_engine->m_eventBufferHeader.InsertFirst(*eventBuffer);

Exit:
    g_lock.Unlock();

    return ec;
}

//
// add window into the stack, the stack does not record the window ref
//
ECode GrafixObjectStack::AddWindow(
    /* [in] */ CGrafixObject* window)
{
    Int32 index;
    CGrafixObject* other;

    assert(GRAFIXOBJECT_SIGN == window->m_sign);

    g_lock.Lock();

    assert(!window->Visible());

    // Iterate from bottom to top,
    // stopping at the first window with a higher priority.
    //
    UInt32 windowPriority = window->GetPriority();
    Void* p = NULL;

    fusion_vector_foreach_reverse(p, index, m_windows)
    {
        other = (CGrafixObject *)p;
        assert(other);

        if (other->GetPriority() <= windowPriority)
            break;
    }

    // Insert the window at the acquired position.
    //
    FusionVectorInsert(&m_windows, window, index + 1);

    GFX_EXPRESSION(DUMPSTACK, {
        kprintf("add window 0x%08x %S\n", window, window->m_title);
        Dump();}
    );

    m_grafixObjectStackChanged = TRUE;

    g_lock.Unlock();

    return NOERROR;
}

Void GrafixObjectStack::RemoveWindow(
    /* [in] */ CGrafixObject* window)
{
    assert(window);
    assert(GRAFIXOBJECT_SIGN == window->m_sign);

    Void* p;
    Int32 index;

    g_lock.Lock();

    assert(window->m_stack);
    assert(!window->m_alive);
    assert(window != m_focusedWindow);
    assert(window != m_pointerWindow);
    assert(window != m_enteredWindow);
    assert(window != m_keyboardWindow);

    index = FusionVectorIndexOf(&m_windows, window);
    // when initialize failed, the index equal -1
    //
    if (-1 != index) {
        FusionVectorRemove(&m_windows, index);
    }

    fusion_vector_foreach(p, index, m_windows) {
        if (((CGrafixObject *)p)->m_ownerGrafixObject == window) {
            ((CGrafixObject *)p)->m_ownerGrafixObject = NULL;
        }
    }

    window->m_stack = NULL;

    assert(-1 == FusionVectorIndexOf(&m_windows, window));

    m_grafixObjectStackChanged = TRUE;

    g_lock.Unlock();
}

ECode GrafixObjectStack::CleanWindowResource(
    /* [in] */ CGrafixObject* window)
{
    assert(window);
    assert(GRAFIXOBJECT_SIGN == window->m_sign);

    Void* p;
    Int32 index;
    ECode ec = NOERROR;

    g_lock.Lock();

    if (!window->m_alive) {
        goto Exit;
    }

    assert(window->m_stack);

    // set window unvisible
    //
    if (window->m_surface->m_flipRectangle &&
            !window->m_surface->m_flipRectangle->IsEmpty()) {
        assert(window->m_visible);
        if (TransitionEffect_None != window->m_trailingEffect) {
            window->m_animationQualification = AnimationQualification_Hide;
        }
        window->m_animationFeasibility = AnimationFeasibility_Hide;
        window->m_surface->m_flipRectangle->RemoveAll();
    }

    window->m_visible = FALSE;

    if (window->m_visibleSwitch == GrafixObjectVisibleSwitch_Exposed) {
        assert(!window->m_exposed);
        window->m_visibleSwitch = GrafixObjectVisibleSwitch_None;

        window->m_needPaint = FALSE;
        if (PaintStatus_Begin == window->m_paintStatus) {
            window->m_paintStatus = PaintStatus_NotNeed;
            window->m_animationFeasibility = AnimationFeasibility_None;
        }
    }
    else if (window->m_visibleSwitch == GrafixObjectVisibleSwitch_None
            && window->m_exposed) {
        window->m_visibleSwitch = GrafixObjectVisibleSwitch_CoveredUp;
        window->NotifyEventBuffer(NotifyType_Shine);
    }

    // switch the focus window
    //
    if (window == m_focusedWindow) {
        m_focusedWindow = NULL;
        ec = UpdateFocus();
        if (GFX_FAILED(ec)) {
            goto Exit;
        }
    }

    assert(window != m_focusedWindow);

    if (window == m_pointerWindow) {
        m_pointerWindow = NULL;
    }

    if (window == m_keyboardWindow) {
        m_keyboardWindow = NULL;
    }

    if (window == m_enteredWindow) {
        m_enteredWindow = NULL;
    }

    assert(window != m_pointerWindow);
    assert(window != m_enteredWindow);
    assert(window != m_keyboardWindow);

    window->SetOwner(NULL);

    // clean time event
    //
    index = FusionVectorIndexOf(&m_timeWindows, window);
    if (-1 != index) {
        FusionVectorRemove(&m_timeWindows, index);
    }

    // clean grab key
    //
    fusion_vector_foreach_reverse(p, index, m_grabbedKeys)
    {
        if (((GrabbedKey *)p)->grabber == window) {
            FusionVectorRemove(&m_grabbedKeys, index);

            free(p);
        }
    }

    // clean interception key
    //
    fusion_vector_foreach_reverse(p, index, m_interceptedKeys)
    {
        if (((InterceptedKey *)p)->intercepter == window) {
            FusionVectorRemove(&m_interceptedKeys, index);

            free(p);
        }
    }

    // clean key cache
    //
    for (index = 0; index < KEY_CACHE; index++) {
        if (m_keys[index].owner == window) {
            m_keys[index].owner = NULL;
            m_keys[index].keyValue = -1;
        }
    }

    assert(window->m_alive);

    // Release the resource in the destructor
    //
    if (window->m_surface) {
        window->m_surface->DisableSurface();
    }

    // clean event buffer
    //
    if (!(window->m_config.option & GrafixAttribute_Ghost)) {
        window->CleanEventBuffer();
    }
#ifdef _DEBUG
    else {
        assert(NULL == window->m_eventBuffer);
    }
#endif

    window->m_alive = FALSE;

Exit:
    g_lock.Unlock();

    return ec;
}

Void GrafixObjectStack::GetMainWindow(
    /* [in] */ Int32 appletID,
    /* [out] */ CGrafixObject** owner)
{
    Int32 index;
    Void* p = NULL;

    assert(owner);

    *owner = NULL;

    g_lock.Lock();

    fusion_vector_foreach(p, index, m_windows)
    {
        CGrafixObject* other = (CGrafixObject *)p;
        assert(other);

        if (other->m_appletId == appletID
            && GrafixObjectType_Main == other->m_type) {
            assert(!(*owner));
            *owner = other;
#ifndef _DEBUG
            break;
#endif
        }
    }

    g_lock.Unlock();
}

ECode GrafixObjectStack::UpdateFocus()
{
    CGrafixObject* after = NULL;
    ECode ec = NOERROR;

    g_lock.Lock();

    Void* element;
    Int32 index;

    fusion_vector_foreach_reverse(element, index, m_windows)
    {
        CGrafixObject* subWindow = (CGrafixObject *)element;

        if (subWindow->Visible()
//            && subWindow->m_enableInput   // CHECKME: whether disabled window can be active
            && !(subWindow->m_config.option & GrafixAttribute_NoFocus)) {
            after = subWindow;
            break;
        }
    }

    if (m_focusedWindow != after) {
        // switch focus and send enter event
        //
        ec = SwitchFocus(after);
        if (GFX_FAILED(ec)) {
            goto Exit;
        }
    }

Exit:
    g_lock.Unlock();

    return ec;
}

ECode GrafixObjectStack::SwitchFocus(
    /* [in] */ CGrafixObject* after)
{
    GrafixObjectEvent evt;
    ECode ec = NOERROR;
    CGrafixObject* preMainWindow = NULL;
    CGrafixObject* afterMainWindow = NULL;

    g_lock.Lock();

    if (m_focusedWindow == after) {
        goto Exit;
    }

    if (m_focusedWindow && after) {
        if (m_focusedWindow->m_appletId != after->m_appletId) {
            GetMainWindow(m_focusedWindow->m_appletId, &preMainWindow);
            assert(preMainWindow);

            GetMainWindow(after->m_appletId, &afterMainWindow);
            assert(afterMainWindow);
        }
    }
    else if (m_focusedWindow) {
        GetMainWindow(m_focusedWindow->m_appletId, &preMainWindow);
        assert(preMainWindow);
    }
    else {
        assert(after);

        GetMainWindow(after->m_appletId, &afterMainWindow);
        assert(afterMainWindow);
    }


    if (m_focusedWindow) {
        evt.type = GrafixObjectEventType_LostFocus;
        GFX_EXPRESSION_TIP(m_focusedWindow->AddEvent(evt));

        if (preMainWindow) {
            evt.type = GrafixObjectEventType_ApplicationDeactived;
            GFX_EXPRESSION_TIP(preMainWindow->AddEvent(evt));
        }
    }

    if (after) {
        evt.type = GrafixObjectEventType_GotFocus;
        GFX_EXPRESSION_TIP(after->AddEvent(evt));

        if (afterMainWindow) {
            evt.type = GrafixObjectEventType_ApplicationActived;
            GFX_EXPRESSION_TIP(afterMainWindow->AddEvent(evt));
        }
    }

    GFX_EXPRESSION(DEBUGLEVEL, {
        kprintf("Switch focus from ");
        if (m_focusedWindow) {
            kprintf("\"%S(0x%08x)\" ", m_focusedWindow->m_title,
                                    m_focusedWindow);
        }
        else {
            kprintf("NULL ");
        }
        kprintf("to ");
        if (after) {
            kprintf("\"%S(0x%08x)\"\n", after->m_title, after);
        }
        else {
            kprintf("NULL\n");
        }
    }
    );

    m_focusedWindow = after;

Exit:
    g_lock.Unlock();

    return ec;
}

ECode GrafixObjectStack::GetPointWindow(
    /* [in] */ Boolean order,
    /* [in] */ Int32 exceptId,
    /* [out] */ CGrafixObject** pointWindow)
{
    Void* p;
    CGrafixObject* window;
    Int32 i, x, y;

    assert(pointWindow);

    g_lock.Lock();

    *pointWindow = NULL;

    if (m_pointerWindow) {
        *pointWindow = m_pointerWindow;

        goto Exit;
    }

    if (!m_cursor || order) {
        fusion_vector_foreach_reverse(p, i, m_windows)
        {
            window = (CGrafixObject *) p;
            if (window->Visible()
                && window->m_objId != exceptId
                && !(window->m_config.option & GrafixAttribute_Ghost)) {
                *pointWindow = window;

                break;
            }
        }

        goto Exit;
    }

    x = m_cursor->m_config.bounds.x;
    y = m_cursor->m_config.bounds.y;

    fusion_vector_foreach_reverse(p, i, m_windows)
    {
        window = (CGrafixObject *)p;

        if (window->Visible()
            && window->m_objId != exceptId
            && !(window->m_config.option & GrafixAttribute_Ghost)
            && (PointInRectangle(&window->m_config.bounds, x, y)
            || PointInRectangle(&window->m_config.activeArea, x, y))) {
            *pointWindow = window;

            break;
        }
    }

Exit:
    g_lock.Unlock();

    return NOERROR;
}

ECode GrafixObjectStack::GetFoucusWindow(
    /* [in] */ Boolean order,
    /* [out] */ CGrafixObject** focusWindow)
{
    Void* p;
    CGrafixObject* window;
    Int32 i, x, y;

    assert(focusWindow);

    g_lock.Lock();

    *focusWindow = NULL;

    if (!m_cursor || order) {
        fusion_vector_foreach_reverse(p, i, m_windows)
        {
            window = (CGrafixObject *)p;
            if (window->Visible()
                && !(window->m_config.option & GrafixAttribute_Ghost)
                && !(window->m_config.option & GrafixAttribute_NoFocus)) {
                *focusWindow = window;

                break;
            }
        }

        goto Exit;
    }

    x = m_cursor->m_config.bounds.x;
    y = m_cursor->m_config.bounds.y;

    fusion_vector_foreach_reverse(p, i, m_windows)
    {
        window = (CGrafixObject *)p;
        Rectangle * bounds = &window->m_config.bounds;

        if (window->Visible()
            && !(window->m_config.option & GrafixAttribute_Ghost)
            && !(window->m_config.option & GrafixAttribute_NoFocus)
            && PointInRectangle(bounds, x, y)) {
            *focusWindow = window;

            break;
        }
    }

Exit:
    g_lock.Unlock();

    return NOERROR;
}

ECode GrafixObjectStack::PostButtonEvent(
    /* [in] */ GrafixObjectEvent& event)
{
    CGrafixObject* window;
    Int32 dx = 0, dy = 0;
    ECode ec = NOERROR;

    g_lock.Lock();

    m_haveInput = TRUE;

    Int32 originalx = event.absoluteX, originaly = event.absoluteY;

    event.absoluteX = originalx * g_transformMatrix[m_orientation].xx
        + originaly * g_transformMatrix[m_orientation].yx
        + g_transformMatrix[m_orientation].x0;
    event.absoluteY = originalx * g_transformMatrix[m_orientation].xy
        + originaly * g_transformMatrix[m_orientation].yy
        + g_transformMatrix[m_orientation].y0;

    if (event.type != GrafixObjectEventType_MultiTouch) {
        dx = event.absoluteX - m_cursor->m_config.bounds.x;
        dy = event.absoluteY - m_cursor->m_config.bounds.y;

        ec = m_cursor->Move(dx, dy);
        if (GFX_FAILED(ec)) {
            goto Exit;
        }
    }

    switch (event.type) {
        case GrafixObjectEventType_StylusPressed:
            window = m_enteredWindow;

            // find the enter window
            //
            ec = GetPointWindow(FALSE, event.objId, &m_enteredWindow);
            if (GFX_FAILED(ec)) {
                goto Exit;
            }

            if (m_enteredWindow) {
                if (!m_enteredWindow->m_enableInput) {
                    GFX_EXPRESSION(WARNING,
                        kprintf("window %S disable input\n",
                        m_enteredWindow->m_title));

                    goto Exit;
                }
                // if window has not been painted, do not repond mouse events
                //
                else if (m_pointerWindow != m_enteredWindow
                    && m_enteredWindow->m_paintStatus != PaintStatus_Completed){

                    GFX_EXPRESSION(WARNING,
                        kprintf("ignore mouse events for window %S not painted\n",
                        m_enteredWindow->m_title));

                    m_enteredWindow = NULL;
                    goto Exit;
                }
            }

            if (m_enteredWindow && !(event.objId)) {
                ec = Restack(m_enteredWindow);
                if (GFX_FAILED(ec)) {
                    goto Exit;
                }
            }

        case GrafixObjectEventType_StylusLifted:
        case GrafixObjectEventType_StylusReleased:
        case GrafixObjectEventType_StylusHeld:
            window = m_pointerWindow ? m_pointerWindow : m_enteredWindow;
            if (window) {
                if (!window->m_enableInput) {
                    GFX_EXPRESSION(WARNING,
                        kprintf("window %S disable input\n",
                        m_enteredWindow->m_title));

                    goto Exit;
                }

                event.relativeX = event.absoluteX - window->m_config.bounds.x;
                event.relativeY = event.absoluteY - window->m_config.bounds.y;

                ec = window->AddEvent(event);
                if (GFX_FAILED(ec)) {
                    goto Exit;
                }
            }

            break;

        case GrafixObjectEventType_StylusMove:
        case GrafixObjectEventType_MultiTouch:
            window = m_pointerWindow ? m_pointerWindow : m_enteredWindow;
            if (window) {
                if (!window->m_enableInput) {
                    GFX_EXPRESSION(WARNING,
                        kprintf("window %S disable input\n",
                        m_enteredWindow->m_title));

                    goto Exit;
                }

                switch (window->m_status) {
                    case GrafixObjectProperty_Moveable:
                        ec = window->Move(dx, dy);
                        if (GFX_FAILED(ec)) {
                            goto Exit;
                        }

                        break;

                    default:
                        event.relativeX =
                                event.absoluteX - window->m_config.bounds.x;
                        event.relativeY =
                                event.absoluteY - window->m_config.bounds.y;

                        ec = window->AddEvent(event);
                        if (GFX_FAILED(ec)) {
                            goto Exit;
                        }
                }
            }

            break;

        default:
            assert(0 && "impossible");
    }

Exit:
    g_lock.Unlock();

    return ec;
}

ECode GrafixObjectStack::PostKeyEvent(
    /* [in] */ GrafixObjectEvent& event)
{
    CGrafixObject* keyboardWindow;
    Int32 index;
    Int32 index1;
    Void* p;
    ECode ec = NOERROR;
    Boolean intercepted = FALSE;

#ifdef _DEBUG
    Boolean bGrabbed = FALSE;
#endif

    g_lock.Lock();

    m_haveInput = TRUE;

    // post the key to the interception window
    //
    fusion_vector_foreach(p, index, m_interceptedKeys)
    {
        InterceptedKey    * intercept = (InterceptedKey *)p;

        if (intercept->keyValue == event.keyValue
            && intercept->modifier == event.modifier
            && intercept->intercepter->m_enableInput) {
            ec = intercept->intercepter->AddEvent(event);
            if (GFX_FAILED(ec)) {
                goto Exit;
            }

            intercepted = TRUE;
        }
    }

    // post the key to grabbed window, if have not the window grabbed this
    // key, post the key to the focus window
    //
    fusion_vector_foreach(p, index, m_grabbedKeys)
    {
        GrabbedKey* grab = (GrabbedKey *)p;

        if (grab->keyValue == event.keyValue
            && grab->modifier == event.modifier) {
            assert(!bGrabbed);

            if (grab->grabber->m_enableInput) {
                if (intercepted) {
                    fusion_vector_foreach(p, index1, m_interceptedKeys)
                    {
                        InterceptedKey* interception = (InterceptedKey *)p;
                        if (grab->grabber == interception->intercepter
                            && grab->keyValue == interception->keyValue
                            && grab->modifier == interception->modifier) {
#ifdef _DEBUG
                            goto CONTINUE;
#else
                            goto Exit;
#endif
                        }
                    }
                }

                ec = grab->grabber->AddEvent(event);
                if (GFX_FAILED(ec)) {
                    goto Exit;
                }
            }

#ifdef _DEBUG
CONTINUE:
            bGrabbed = TRUE;
#else
            goto Exit;
#endif
        }
    }

#ifdef _DEBUG

    if (bGrabbed) {
        goto Exit;
    }

#endif

    GetKeyboardWindow(event, &keyboardWindow);
    if (!keyboardWindow) {
        goto Exit;
    }

    if (!keyboardWindow->m_enableInput) {
        goto Exit;
    }

#ifdef _DEBUG
    // It's for window move
    //
    if (VK_LSHIFT == event.keyValue || VK_Lock == event.keyValue) {
        if (GrafixObjectEventType_KeyDown == event.type) {
            ec = keyboardWindow->SetProperty(GrafixObjectProperty_Moveable);
            if (GFX_FAILED(ec)) {
                goto Exit;
            }
        }
        else {
            ec = keyboardWindow->SetProperty(GrafixObjectProperty_None);
            if (GFX_FAILED(ec)) {
                goto Exit;
            }
        }
    }

    // Dump window information
    //
    if (VK_VOLUME_UP == event.keyValue
            && GrafixObjectEventType_KeyHeld == event.type) {
        Dump();
        g_bufferCache.Dump();
    }
#endif

    if (intercepted) {
        fusion_vector_foreach(p, index, m_interceptedKeys)
        {
            InterceptedKey* intercept = (InterceptedKey *)p;

            if (intercept->intercepter == keyboardWindow
                && intercept->keyValue == event.keyValue
                && intercept->modifier == event.modifier) {
                goto Exit;
            }
        }
    }

    ec = keyboardWindow->AddEvent(event);
    if (GFX_FAILED(ec)) {
        goto Exit;
    }

Exit:
    g_lock.Unlock();

    return ec;
}

ECode GrafixObjectStack::Update(
    /* [in] */ Region* updateRegion,
    /* [in] */ Int32 end)
{
    Region region;

    Rectangle screen = {0, 0, m_width, m_height};

    if (!updateRegion) {
        RegionFromRectangle(&region, &screen);
    }
    else {
        region = *updateRegion;
    }

    if (!RegionRectangleIntersect(&region, &screen)) {
        return NOERROR;
    }

    g_lock.Lock();

    ECode ec = UpdateRegion(m_windows.count - 1, end, &region);
    if (GFX_FAILED(ec)) {
        goto Exit;
    }

Exit:
    g_lock.Unlock();

    RenderDriver(&region);

    return ec;
}

Void GrafixObjectStack::AssurePriority(
    /* [in] */ Int32 begion)
{
    if (begion < 0) {
        begion = 0;
    }

    Int32 current = begion;

    while (current < m_windows.count - 1) {
        CGrafixObject* currentWindow = (CGrafixObject *)FusionVectorAt(
                        &m_windows, current);
        CGrafixObject* aboveWindow = (CGrafixObject *)FusionVectorAt(
                        &m_windows, current + 1);

        if (currentWindow->GetPriority() > aboveWindow->GetPriority()) {
            FusionVectorMove(&m_windows, current, current + 1);
            return AssurePriority(current - 1);
        }

        current++;
    }
}

Int32 GrafixObjectStack::AdjustWindowFloor(
    /* [in] */ Int32 oldIndex,
    /* [in] */ Int32 newIndex)
{
    assert(oldIndex >= 0 && oldIndex <= m_windows.count - 1);

    CGrafixObject* window = (CGrafixObject *)FusionVectorAt(&m_windows, oldIndex);
    UInt32 windowPriority = window->GetPriority();

    if (-1 == newIndex) {
        newIndex = m_windows.count - 1;
    }

    assert(newIndex >= 0 && newIndex <= m_windows.count - 1);

    // Assure window won't be above any window with a higher priority.
    //
    while (newIndex > 0) {
        // find the window which blew the window new index
        //
        Int32 below = (oldIndex < newIndex) ? newIndex : newIndex - 1;
        CGrafixObject* other = (CGrafixObject *)FusionVectorAt(&m_windows, below);
        assert(other);
        assert(below != oldIndex);

        if (windowPriority < other->GetPriority()
                || window->IsOwner(other)) {
            newIndex--;
        }
        else {
            break;
        }
    }

    // Assure window won't be below any window with a lower priority.
    //
    while (newIndex < m_windows.count - 1) {
        Int32 above = (oldIndex > newIndex) ? newIndex : newIndex + 1;
        CGrafixObject* other = (CGrafixObject *)FusionVectorAt(&m_windows, above);
        assert(other);

        if (windowPriority > other->GetPriority()) {
            newIndex++;
        }
        else {
            break;
        }
    }

    if (oldIndex == newIndex) {
        return newIndex;
    }

    // Actually change the stacking order now.
    //
    FusionVectorMove(&m_windows, oldIndex, newIndex);

    return newIndex;
}

Void  GrafixObjectStack::Dump()
{
    int index;
    Void* node;
    static int s_count;

    kprintf("DumpStack: %d\n", s_count++);

    fusion_vector_foreach_reverse(node, index, m_windows) {
        CGrafixObject* window = (CGrafixObject *)node;
        kprintf("%d(%S): window = 0x%08x, priority = %x, owner = 0x%08x\n",
                    index,
                    window->m_title,
                    window,
                    window->GetPriority(),
                    window->m_ownerGrafixObject);

        kprintf("\tbounds: %d, %d, %d, %d   visible:%d, alived: %d, type: %d\n",
                    RECTANGLE_VALS(&window->m_config.bounds),
                    window->Visible(),
                    window->m_alive,
                    window->m_type);

        kprintf("\tcache buffer 0x%08x, applet: 0x%08x, application: 0x%08x, userHandle: 0x%08x\n",
                    window->m_surface->m_cacheBuffer,
                    window->m_appletId,
                    window->m_userDefinedHandle,
                    window->m_userDefinedId);

        if (!window->m_surface->m_flipRectangle->IsEmpty()) {
            kprintf("flip:");

            RectangleLink* node;
            Int32 count = 0;

            ForEachDLinkNode(RectangleLink*,
                    node, window->m_surface->m_flipRectangle) {
                if (!node->m_free) {
                    kprintf("\t%d: %d, %d, %d, %d\n",
                        count, RECTANGLE_VALS(&node->m_rect));

                    count++;
                }
            }

            assert(count == window->m_surface->m_flipRectangle->m_rect.x);
        }

        kprintf("\n");
    }

    kprintf("\n");
}

Void  GrafixObjectStack::Check(
    /* [in] */ CGrafixObject* window)
{
    Int32 index = 0;
    UInt32 priority = 0;

    while (index < m_windows.count) {
        CGrafixObject* currentWindow = (CGrafixObject*)FusionVectorAt(&m_windows, index);

        if (currentWindow->m_ownerGrafixObject) {
            Boolean foundOwner = FALSE;
            for (int i = 0; i < index; i++) {
                CGrafixObject* ownerWindow = (CGrafixObject*)FusionVectorAt(&m_windows, i);

                if (currentWindow->m_eventBuffer && ownerWindow->m_eventBuffer
                        && currentWindow->m_eventBuffer
                            == ownerWindow->m_eventBuffer) {
                    assert(currentWindow->m_contextId
                            == ownerWindow->m_contextId);
                }

                if (ownerWindow == currentWindow->m_ownerGrafixObject) {
                    foundOwner = TRUE;
                    break;
                }
            }

            if (!foundOwner) {
                goto Error;
            }
        }

        if (!currentWindow->m_surface->m_flipRectangle->IsEmpty()
                && !currentWindow->m_surface->m_cacheBuffer) {
            kprintf("The window buffer is mistaken\n");

            goto Error;
        }

        if (priority > currentWindow->GetPriority()) {
            kprintf("The window priority is mistaken\n");

            goto Error;
        }
        else {
            priority = currentWindow->GetPriority();
        }

        index++;
    }

    return;

Error:
    kprintf("restack window 0x%08x %S\n", window, window->m_title);
    Dump();
    DebugBreak();
}

ECode GrafixObjectStack::Restack(
    /* [in] */ CGrafixObject* window,
    /* [in] */ Boolean up)
{
    assert(window);

    if (!up) {
        GFX_ERROR_MESSAGE("now, not support lower to bottom\n");

        return E_INVALID_OPERATION;
    }

    CGrafixObject** movedWindow;
    Int32 count = 0;
    Int32 i;
    ECode ec = NOERROR;

    g_lock.Lock();

    movedWindow = (CGrafixObject **)_alloca(m_windows.count * sizeof(CGrafixObject *));
    if (!movedWindow) {
        GFX_ERROR_MESSAGE("Alloc space on the stack failed\n");
        ec = E_OUT_OF_MEMORY;

        goto Exit;
    }

    memset(movedWindow, 0, m_windows.count * sizeof(CGrafixObject *));

    if (up) {
        Int32 oldFloor;
        Int32 newFloor;

        // assure the priority is right;
        //
        AssurePriority(0);

//        Int32 appletID = window->m_appletId;
//
//        // The appliation all window
//        //
//        oldFloor = 0;
//        while (oldFloor < m_windows.count) {
//            CGrafixObject* other = (CGrafixObject *)FusionVectorAt(&m_windows, oldFloor);
//            if (other->m_appletId == appletID) {
//                Boolean moved = FALSE;
//
//                for (i = 0; i < count; i++) {
//                    if (other == movedWindow[i]) {
//                        moved = TRUE;
//                        break;
//                    }
//                }
//
//                if (moved) {
//                    oldFloor++;
//                }
//                else {
//                    assert(oldFloor == FusionVectorIndexOf(&m_windows, other));
//                    newFloor = AdjustWindowFloor(oldFloor, -1);
//                    assert(!movedWindow[count]);
//                    if (newFloor != oldFloor) {
//                        movedWindow[count++] = other;
//                    }
//                    else {
//                        oldFloor++;
//                    }
//                }
//            }
//            else {
//                oldFloor++;
//            }
//        }
//
//        memset(movedWindow, 0, m_windows.count * sizeof(CGrafixObject *));
//        count = 0;

        // up the restack window
        //
        Int32 oldIndex;
        Int32 newIndex;

        oldIndex = FusionVectorIndexOf(&m_windows, window);
        newIndex = AdjustWindowFloor(oldIndex, -1);
        if (newIndex != oldIndex) {
            movedWindow[count++] = window;
        }

        // up the child window
        //
        oldFloor = 0;

        while (oldFloor < m_windows.count) {
            CGrafixObject* other = (CGrafixObject *)FusionVectorAt(&m_windows, oldFloor);

            if (window->IsOwner(other)) {
                Boolean moved = FALSE;

                for (i = 0; i < count; i++) {
                    if (other == movedWindow[i]) {
                        moved = TRUE;
                        break;
                    }
                }

                if (moved) {
                    oldFloor++;
                }
                else {
                    assert(oldFloor == FusionVectorIndexOf(&m_windows, other));
                    newFloor = AdjustWindowFloor(oldFloor, -1);
                    assert(!movedWindow[count]);
                    if (newFloor != oldFloor) {
                        movedWindow[count++] = other;
                    }
                    else {
                        oldFloor++;
                    }
                }
            }
            else {
                oldFloor++;
            }
        }
    }
    else {
        // TODO: support put the window to bottom
        //
        assert(0);
    }

#ifdef _DEBUG
    Check(window);
#endif

    ec = UpdateFocus();
    if (GFX_FAILED(ec)) {
        goto Exit;
    }

    if (!GetFlip()) {
        goto Exit;
    }

    GFX_EXPRESSION(DUMPSTACK, {
        kprintf("restack 0x%08x %S\n", window, window->m_title);
        Dump();});

    ec = Update();
    if (GFX_FAILED(ec)) {
        goto Exit;
    }

    m_grafixObjectStackChanged = TRUE;

Exit:
    g_lock.Unlock();

    return ec;
}

ECode GrafixObjectStack::UpdateRegion(
    /* [in] */ Int32 start,
    /* [in] */ Int32 end,
    /* [in] */ Region* updateRegion)
{
    assert(updateRegion && RegionRegular(updateRegion));
    assert(end >= 0);
    assert(start < m_windows.count);

    Region windowRegion = *updateRegion;
    CGrafixObject* window;
    ECode ec;

    // Find first intersecting window.
    //
    while (start >= end) {
        window = (CGrafixObject *)FusionVectorAt(&m_windows, start);

        // visible and intersect
        //
        if (window->Visible()
                && RegionRectangleIntersect(&windowRegion,
                &window->m_config.bounds)) {
            break;
        }

        start--;
    }

    // Intersecting window found
    //
    if (start >= end) {
        Region otherRegion[4];

        // update other area that out of this window's region
        //
        Int32 Count = RegionSubRegion(*updateRegion,
                                    windowRegion,
                                    otherRegion);
        for (Int32 j = 0; j < Count; j++) {
            ec = UpdateRegion(start - 1, end, &otherRegion[j]);
            if (GFX_FAILED(ec)) {
                return ec;
            }
        }

        Region transparentRegion = {0, 0, -1, -1};
        Boolean intersection = FALSE;

        if (window->LayerTransparent()) {
            assert(window->m_config.option & GrafixAttribute_GrafixLayerChannel);

            // Find the first transparent layer region in the window update
            // region.
            //
            RectangleLink* node =
                (RectangleLink *)window->m_config.transparentRect.First();
            while (node != &window->m_config.transparentRect) {
                if (!node->m_free) {
                    RegionFromRectangle(&transparentRegion, &node->m_rect);
                    RegionTranslate(&transparentRegion,
                                    window->m_config.bounds.x,
                                    window->m_config.bounds.y);
                    if (RegionIntersect(&transparentRegion, &windowRegion)) {
                        intersection = TRUE;
                        break;
                    }
                }

                node = (RectangleLink *)node->Next();
            }
        }

        if (window->Transparent()) {
            if (intersection) {
                Region subRegion[4];

                // upate the window's region except the found transparent layer
                // region.
                //
                Int32 Count = RegionSubRegion(windowRegion,
                                            transparentRegion,
                                            subRegion);
                for (Int32 j = 0; j < Count; j++) {
                    GFX_EXPRESSION_TIP(
                        UpdateRegion(start, end, &subRegion[j]));
                }

                assert(RegionRegular(&transparentRegion));
                GFX_EXPRESSION_TIP(
                    m_engine->m_layerTransparentRect.PreciseAdd(
                    &transparentRegion));

                GFX_EXPRESSION_TIP(
                    window->Draw(m_primarySurface, &transparentRegion));
            }
            else {
                assert(((window->m_config.option
                            & GrafixAttribute_AlphaChannel)
                        || (window->m_config.option
                            & GrafixAttribute_AlphaPixel)));
                GFX_EXPRESSION_TIP(
                    UpdateRegion(start - 1, end, &windowRegion));

                GFX_EXPRESSION_TIP(
                    window->Draw(m_primarySurface, &windowRegion));
            }
        }
        else {
            if (RegionRegular(&transparentRegion)) {
                GFX_EXPRESSION_TIP(
                    m_engine->m_layerTransparentRect.PreciseAdd(
                    &transparentRegion));
            }

            GFX_EXPRESSION_TIP(window->Draw(m_primarySurface, &windowRegion));
        }
    }
    else if (start < 0) {
        GFX_EXPRESSION_TIP(DrawBackground(&windowRegion));
    }

    return NOERROR;
}

Boolean GrafixObjectStack::GetFlip(
    /* [in] */ Int32 end)
{
    Int32 index;
    Boolean changed = FALSE;

    g_lock.Lock();

    for (index = m_windows.count - 1; index >= end; index--) {
        CGrafixObject* window = (CGrafixObject *)m_windows.elements[index];

        if (window->m_alive) {
            assert(window->m_surface->m_flipRectangle->IsEmpty()
                || window->m_surface->m_cacheBuffer);

            window->m_surface->m_flipRectangleRecord->Clone(
                    window->m_surface->m_flipRectangle);
            window->m_surface->m_flipRectangle->RemoveAll();

            if (window->Transparent()) {
                window->m_surface->m_directVisibleRectangleRecord->Clone(
                    window->m_surface->m_directVisibleRectangle);
            }

            window->m_surface->m_directVisibleRectangle->RemoveAll();
        }
    }

    Region update = {0, 0, m_width, m_height};

    UpdateFlip(m_windows.count - 1, end, &update);
    UpdateDirectVisible(m_windows.count - 1, end, &update);

    for (index = m_windows.count - 1; index >= end; index--) {
        CGrafixObject* window = (CGrafixObject *)m_windows.elements[index];

        if (window->m_alive) {
            if (!window->m_surface->m_flipRectangle->Equal(
                    window->m_surface->m_flipRectangleRecord)) {
                changed = TRUE;

                // from hiden state to shown state
                //
                if (window->m_surface->m_flipRectangleRecord->IsEmpty()) {
                    assert(window->m_config.bounds.width
                        == window->m_surface->m_bornRectangle.width);
                    assert(window->m_config.bounds.height
                        == window->m_surface->m_bornRectangle.height);

                    if (!window->m_surface->m_cacheBuffer) {
                        assert(!window->m_render);
                        g_bufferCache.GetBufferNode(
                                        window->m_surface,
                                        window->m_config.bounds.width,
                                        window->m_config.bounds.height,
                                        window->m_surface->m_attribute,
                                        window->m_surface->m_format,
                                        &window->m_surface->m_cacheBuffer);

                        //
                        // If the failed, the window cannot be show, the reason
                        // may be not enough memory, the program can not debug
                        // in the engine.
                        //

                        window->m_surface->ChangeSurfaceBuffer();
                    }
                    else if (!window->m_render) {
                        window->m_surface->m_cacheBuffer->AddRef();
                    }

                    window->m_needPaint = TRUE;
                    window->NotifyEventBuffer(NotifyType_Paint);

                    // it means that the window is at coveredup state, but has
                    // not been handled by form. now it is shown again. so do
                    // not invoke exposed event.
                    //
                    if (GrafixObjectVisibleSwitch_CoveredUp
                            == window->m_visibleSwitch) {
                        assert(window->m_exposed);
                        window->m_visibleSwitch = GrafixObjectVisibleSwitch_None;

                        // exposed event is not handled, the window is coveredup,
                        // so do not invoke paint event.
                        //
                        window->m_needPaint = FALSE;
                        if (PaintStatus_Begin == window->m_paintStatus) {
                            window->m_paintStatus = PaintStatus_NotNeed;
                            window->m_animationFeasibility = AnimationFeasibility_None;
                        }
                    }
                    // otherwise, the window is coveredup, invoke exposed event.
                    //
                    else if (!window->m_exposed) {
                        window->m_visibleSwitch = GrafixObjectVisibleSwitch_Exposed;
                        window->NotifyEventBuffer(NotifyType_Shine);
                    }

                    window->m_animationFeasibility = AnimationFeasibility_Show;
                    window->m_render = TRUE;

                    window->AddRef();
                }

                // from shown state to hiden state
                //
                if (window->m_surface->m_flipRectangle->IsEmpty()) {
                    // it means that there is an exposed event in event buffer
                    // which is not handled. but here, this window will hide
                    // again, so it unnecessarily invokes covered event.
                    //
                    if (GrafixObjectVisibleSwitch_Exposed == window->m_visibleSwitch){
                        assert(!window->m_exposed);
                        window->m_visibleSwitch = GrafixObjectVisibleSwitch_None;

                        window->m_needPaint = FALSE;
                        if (PaintStatus_Begin == window->m_paintStatus) {
                            window->m_paintStatus = PaintStatus_NotNeed;
                            window->m_animationFeasibility = AnimationFeasibility_None;
                        }
                    }
                    // otherwise, if the window has exposed, notify covered up
                    // event.
                    //
                    else if (window->m_exposed) {
                        window->m_visibleSwitch = GrafixObjectVisibleSwitch_CoveredUp;
                        window->NotifyEventBuffer(NotifyType_Shine);
                    }

                    window->m_animationFeasibility = AnimationFeasibility_Hide;
                }

                continue;
            }
            else if (!changed && window->Transparent()
                    && !window->m_surface->m_directVisibleRectangle->Equal(
                        window->m_surface->m_directVisibleRectangleRecord)) {
                changed = TRUE;
            }
        }
    }

    g_lock.Unlock();

    return changed;
}

Void GrafixObjectStack::UpdateFlip(
    /* [in] */ Int32 start,
    /* [in] */ Int32 end,
    /* [in] */ Region* updateRegion)
{
    assert(updateRegion && RegionRegular(updateRegion));
    assert(end <= start);
    assert(end >= 0);
    assert(start < m_windows.count);

    Region windowRegion = *updateRegion;
    CGrafixObject* window = NULL;

    // Find first intersecting window.
    //
    while (start >= end) {
        window = (CGrafixObject *)FusionVectorAt(&m_windows, start);

        // visible and intersect
        //
        if (window->Visible()
                && RegionRectangleIntersect(&windowRegion,
                    &window->m_config.bounds)) {
            break;
        }

        start--;
    }

    if (start > end) {
        Region transparentRegion;
        Boolean intersection = FALSE;

        if (window->LayerTransparent()) {
            RectangleLink* node =
                (RectangleLink *)window->m_config.transparentRect.First();
            while (node != &window->m_config.transparentRect) {
                if (!node->m_free) {
                    RegionFromRectangle(&transparentRegion, &node->m_rect);
                    RegionTranslate(&transparentRegion,
                                    window->m_config.bounds.x,
                                    window->m_config.bounds.y);
                    if (RegionIntersect(&transparentRegion, &windowRegion)) {
                        intersection = TRUE;
                        break;
                    }
                }

                node = (RectangleLink *)node->Next();
            }
        }

        if (intersection) {
            Region subRegion[4];

            int Count = RegionSubRegion(*updateRegion,
                                        transparentRegion,
                                        subRegion);
            for (int j = 0; j < Count; j++) {
                UpdateFlip(start, end, &subRegion[j]);
            }

            window->m_surface->m_flipRectangle->PreciseAdd(&transparentRegion);
        }
        else if (window->Transparent()) {
            UpdateFlip(start - 1, end, updateRegion);

            window->m_surface->m_flipRectangle->PreciseAdd(&windowRegion);
        }
        else {
            Region subRegion[4];

            int Count = RegionSubRegion(*updateRegion,
                                        windowRegion,
                                        subRegion);
            for (int j = 0; j < Count; j++) {
                UpdateFlip(start - 1, end, &subRegion[j]);
            }

            window->m_surface->m_flipRectangle->PreciseAdd(&windowRegion);
        }
    }
    else if (start == end) {
        window->m_surface->m_flipRectangle->PreciseAdd(&windowRegion);
    }
}

Void GrafixObjectStack::UpdateDirectVisible(
    /* [in] */ Int32 start,
    /* [in] */ Int32 end,
    /* [in] */ Region* updateRegion)
{
    assert(updateRegion && RegionRegular(updateRegion));
    assert(end <= start);
    assert(end >= 0);
    assert(start < m_windows.count);

    Region windowRegion = *updateRegion;
    CGrafixObject* window = NULL;

    // Find first intersecting window.
    //
    while (start >= end) {
        window = (CGrafixObject *)FusionVectorAt(&m_windows, start);

        // visible and intersect
        //
        if (window->Visible()
            && RegionRectangleIntersect(&windowRegion,
            &window->m_config.bounds)) {
            break;
        }

        start--;
    }

    if (start > end) {
        Region subRegion[4];

        int Count = RegionSubRegion(*updateRegion,
                                    windowRegion,
                                    subRegion);
        for (int j = 0; j < Count; j++) {
            UpdateDirectVisible(start - 1, end, &subRegion[j]);
        }

        window->m_surface->m_directVisibleRectangle->PreciseAdd(&windowRegion);
    }
    else if (start == end) {
        window->m_surface->m_directVisibleRectangle->PreciseAdd(&windowRegion);
    }
}

ECode GrafixObjectStack::DrawBackground(
    /* [in] */ Region* region)
{
    assert(region);

    Rectangle rectangle = RECTANGLE_INIT_FROM_REGION(region);

    m_engine->m_renderRect.PreciseAdd(region);

    Buffer* dstBuffer = &m_primarySurface->m_cacheBuffer->m_buffer;
    Buffer* srcBuffer = &m_backgroundSurface->m_cacheBuffer->m_buffer;

    RGB24Blit2RGB24(
        dstBuffer->info.address,
            rectangle.x, rectangle.y, dstBuffer->info.pitch,
        srcBuffer->info.address,
            rectangle.x, rectangle.y,
            rectangle.width, rectangle.height,
            srcBuffer->info.pitch,
        0xFF);


    //return m_primarySurface->Blit(m_backgroundSurface, rectangle,
    //                 rectangle.x, rectangle.y, 0);

    return NOERROR;
}

//
// make sure that if window was received the key press event, it would be
// received the release event
//
Void GrafixObjectStack::GetKeyboardWindow(
    /* [in] */ GrafixObjectEvent& event,
    /* [out] */ CGrafixObject** keyWindow)
{
    Int32 i;

    assert(keyWindow);

    *keyWindow = NULL;

    g_lock.Lock();

    // cache the press key and clear it as the key release
    //
    if (event.type == GrafixObjectEventType_KeyDown
            || event.type == GrafixObjectEventType_KeyHeld) {
        Int32 freeKey = -1;
        CGrafixObject* window;

        for (i = 0; i < KEY_CACHE; i++) {
            if (m_keys[i].keyValue == event.keyValue) {
                assert(m_keys[i].owner);
                *keyWindow = m_keys[i].owner;

                goto Exit;
            }

            if (freeKey == -1 && m_keys[i].keyValue == -1) {
                freeKey = i;
            }
        }

        // max press 8 keys at the same time
        //
        if (freeKey == -1) {
            GFX_EXPRESSION(DEBUGLEVEL,
                kprintf("The key cache be not enough struct\n"));

            goto Exit;
        }

        window = m_keyboardWindow ? m_keyboardWindow : m_focusedWindow;

        // no keypad window and focus window
        //
        if (!window) {
            goto Exit;
        }

        m_keys[freeKey].keyValue  = event.keyValue;
        m_keys[freeKey].owner   = window;

        *keyWindow = window;

        goto Exit;
    }
    else {
        // send the release message to the press window
        // if the window be destory, the release will be ignore
        //
        for (i = 0; i < KEY_CACHE; i++) {
            if (m_keys[i].keyValue == event.keyValue) {
                m_keys[i].keyValue = -1;
                assert(m_keys[i].owner);

                *keyWindow = m_keys[i].owner;

                goto Exit;
            }
        }
    }

Exit:
    g_lock.Unlock();
}

Void GrafixObjectStack::MarkAnimationSymbol(
    /* [in] */ Int32 current,
    /* [in] */ Region* updateRegion)
{
    assert(updateRegion && RegionRegular(updateRegion));
    assert(current < m_windows.count);

    Region windowRegion = *updateRegion;
    CGrafixObject* window;

    // Find first intersecting window.
    //
    while (current >= 0) {
        window = (CGrafixObject *)FusionVectorAt(&m_windows, current);

        if (window->m_showing
                || !window->m_surface->m_flipRectangle->IsEmpty()) {
            if (window->m_animationFeasibility
                    && window->m_animationFeasibility
                        == window->m_animationQualification) {
                window->m_animationSymbol = TRUE;
            }
            // visible and intersect
            //
            else if (RegionRectangleIntersect(&windowRegion,
                    &window->m_config.bounds)) {
                break;
            }
        }

        current--;
    }

    // Intersecting window found
    //
    if (current >= 0) {
        Region otherRegion[4];

        // update other area that out of this window's region
        //
        Int32 Count = RegionSubRegion(*updateRegion,
                                    windowRegion,
                                    otherRegion);
        for (Int32 j = 0; j < Count; j++) {
            MarkAnimationSymbol(current - 1, &otherRegion[j]);
        }

        Region transparentRegion = {0, 0, -1, -1};
        Boolean intersection = FALSE;

        if (window->LayerTransparent()) {
            assert(window->m_config.option & GrafixAttribute_GrafixLayerChannel);

            // Find the first transparent layer region in the window update
            // region.
            //
            RectangleLink* node =
                (RectangleLink *)window->m_config.transparentRect.First();
            while (node != &window->m_config.transparentRect) {
                if (!node->m_free) {
                    RegionFromRectangle(&transparentRegion, &node->m_rect);
                    RegionTranslate(&transparentRegion,
                                    window->m_config.bounds.x,
                                    window->m_config.bounds.y);
                    if (RegionIntersect(&transparentRegion, &windowRegion)) {
                        intersection = TRUE;
                        break;
                    }
                }

                node = (RectangleLink *)node->Next();
            }
        }

        if (window->Transparent()) {
            if (intersection) {
                Region subRegion[4];

                // upate the window's region except the found transparent layer
                // region.
                //
                Int32 Count = RegionSubRegion(windowRegion,
                                            transparentRegion,
                                            subRegion);
                for (Int32 j = 0; j < Count; j++) {
                    MarkAnimationSymbol(current, &subRegion[j]);
                }

                window->m_animationSymbol = TRUE;
            }
            else {
                assert(((window->m_config.option
                            & GrafixAttribute_AlphaChannel)
                        || (window->m_config.option
                            & GrafixAttribute_AlphaPixel)));
                MarkAnimationSymbol(current - 1, &windowRegion);

                window->m_animationSymbol = TRUE;
            }
        }
        else {
            window->m_animationSymbol = TRUE;
        }
    }
}

ECode GrafixObjectStack::RenderDriver(
    /* [in] */ Region* region)
{
    Int32 index;
    ECode ec = NOERROR;
    Boolean cartoon = FALSE;

    g_lock.Lock();

#ifdef CURSOR_VISIBLE
    if (m_cursor->m_needPaint
            || PaintStatus_Completed != m_cursor->m_paintStatus) {
        IGrafixSurface* cursorSurface;
        int color = CURSOR_COLOR;

        ec = m_cursor->GetGrafixSurface(&cursorSurface);
        if (GFX_FAILED(ec)) {
            return ec;
        }

        cursorSurface->Clear(color);
        cursorSurface->Release();

        m_cursor->m_needPaint = FALSE;
        m_cursor->m_paintStatus = PaintStatus_Completed;
    }
#endif

    Region updateRegion = REGION_INIT_FROM_RECTANGLE(
                    &m_primarySurface->m_bornRectangle);
    MarkAnimationSymbol(m_windows.count - 2, &updateRegion);

    for (index = m_windows.count - 1; index >= 0; index--) {
        CGrafixObject* window = (CGrafixObject *)m_windows.elements[index];

        if (window->m_animationSymbol) {
            if (PaintStatus_Begin == window->m_paintStatus
                    || window->m_needPaint) {
                GFX_EXPRESSION(DEBUG_PAINT,
                    kprintf("window 0x%08x(%S) need paint, paint status = %d\n",
                    window, window->m_title, window->m_paintStatus));
                ec = E_WINDOW_NEED_PAINT;

                goto Exit;
            }

            if (window->m_animationFeasibility
                    && window->m_animationFeasibility
                        == window->m_animationQualification) {
                cartoon = TRUE;
            }
        }
    }

    assert(m_primarySurface->m_cacheBuffer);

    if (cartoon) {
        RemoveAllAnimationLayer();

        GFX_EXPRESSION(DEBUG_ANIMATION, {
            STATIC Int32 s_count;
            kprintf("\n>>>>>>>>>>>> Transform %d >>>>>>>>>>>>>>>\n",
                s_count++);
            });


        for (index = 0; index < m_windows.count - 1; index++) {
            CGrafixObject* window = (CGrafixObject *)m_windows.elements[index];

#ifdef _DEBUG
            if (window->m_animationFeasibility
                    && (window->m_animationFeasibility
                        == window->m_animationQualification)) {
                assert(window->m_surface->m_cacheBuffer);
            }
#endif

            if (window->m_animationSymbol) {
                GFX_EXPRESSION(DEBUG_ANIMATION,
                    kprintf("Add window 0x%08x(%S) [sticky %d, alpha %d, "
                        "x = %d, y = %d, width = %d, height = %d]\n",
                    window, window->m_title,
                    window->m_config.option
                        & GrafixAttribute_StickyBuffer ? 1 : 0,
                    window->Transparent(),
                    RECTANGLE_VALS(&window->m_config.bounds)));

                ec = AddAnimationLayer(window);
                if (GFX_FAILED(ec)) {
                    GFX_EXPRESSION(DEBUG_PAINT,
                        kprintf("window 0x%08x(%S) need paint\n",
                        window, window->m_title));

                    RemoveAllAnimationLayer();
                    goto Exit;
                }
            }
        }

        GFX_EXPRESSION(DEBUG_ANIMATION,
            kprintf(">>>>>>>>>> add over >>>>>>>>>\n"));

        m_layerChanged = TRUE;
        m_invokeAnimation->Notify(SIGNALED);
    }
    else if (!m_layerChanged) {
        m_engine->Render(m_primarySurface);
    }

    for (index = m_windows.count - 1; index >= 0; index--) {
        CGrafixObject* window = (CGrafixObject *)m_windows.elements[index];

        window->m_animationFeasibility = AnimationFeasibility_None;
        window->m_animationQualification = AnimationQualification_None;

        if (window->m_surface->m_flipRectangle->IsEmpty()) {
            if (window->m_render) {
                assert(window->m_surface->m_cacheBuffer);
                window->m_surface->m_cacheBuffer->Release();

                window->m_render = FALSE;

                window->Release();
            }

            window->m_showing = FALSE;
        }
        else {
            window->m_showing = TRUE;
        }
    }

    GFX_EXPRESSION(DEBUG_BUFFERSIZE, {
        g_bufferCache.Dump();
    });

Exit:
    for (index = m_windows.count - 1; index >= 0; index--) {
        CGrafixObject* window = (CGrafixObject *)m_windows.elements[index];
        window->m_animationSymbol = FALSE;
    }

    g_needFlipAll = FALSE;

    g_lock.Unlock();

    return ec;
}

ECode GrafixObjectStack::AddAnimationLayer(
    /* [in] */ CGrafixObject* window)
{
    ECode ec = NOERROR;
    AnimationLayer* node;

    assert(window->m_surface->m_cacheBuffer);

    Boolean isEmpty = AnimationLayerIsEmpty(&m_animationHeader);

    node = (AnimationLayer *)m_animationHeader.Next();
    while (node != &m_animationHeader) {
        if (!node->alive) {
            break;
        }

        node = (AnimationLayer *)node->Next();
    }

    if (node == &m_animationHeader) {
        node = new AnimationLayer;
        if (!node) {
            GFX_ERROR_MESSAGE("Not enough memory\n");
            ec = E_OUT_OF_MEMORY;

            goto Exit;
        }

        m_animationHeader.InsertLast(node);
    }

    window->m_surface->m_cacheBuffer->AddRef();
    node->buffer = window->m_surface->m_cacheBuffer;

    if (!isEmpty
            && AnimationQualification_Show == window->m_animationQualification
            && AnimationFeasibility_Show == window->m_animationFeasibility) {
        node->effect = window->m_leadingEffect;
        node->leadingOffsetX = window->m_leadingOffsetX;
        node->leadingOffsetY = window->m_leadingOffsetY;
        node->leadingOpacity = window->m_leadingOpacity;
        node->leadingSpeed = window->m_leadingDuration;
        GFX_EXPRESSION(DEBUG_ANIMATION,
            kprintf("\tshow animation 0x%08x, offsetX = %d, offsetY = %d, "
            "opacity = %d, speed = %d\n",
            node->effect, node->leadingOffsetX,
            node->leadingOffsetY, node->leadingOpacity, node->leadingSpeed));
    }
    else if (!isEmpty
            && AnimationQualification_Hide == window->m_animationQualification
            && AnimationFeasibility_Hide == window->m_animationFeasibility) {
        node->effect = window->m_trailingEffect;
        node->trailingOffsetX = window->m_trailingOffsetX;
        node->trailingOffsetY = window->m_trailingOffsetY;
        node->trailingOpacity = window->m_trailingOpacity;
        node->trailingSpeed = window->m_trailingDuration;
        GFX_EXPRESSION(DEBUG_ANIMATION,
            kprintf("\thide animation 0x%08x, offsetX = %d, offsetY = %d, "
            "opacity = %d, speed = %d\n",
            node->effect, node->trailingOffsetX,
            node->trailingOffsetY, node->trailingOpacity, node->trailingSpeed));
    }

    node->x = window->m_config.bounds.x;
    node->y = window->m_config.bounds.y;
    node->width = window->m_config.bounds.width;
    node->height = window->m_config.bounds.height;
    node->opacity = window->m_config.opacity;

    node->alive = TRUE;

Exit:
    return ec;
}

Void GrafixObjectStack::RemoveAllAnimationLayer()
{
    g_lock.Lock();

    AnimationLayer* node = (AnimationLayer *)m_animationHeader.Next();
    while (node != &m_animationHeader) {
        if (node->alive) {
            node->alive = FALSE;
            node->effect = TransitionEffect_None;
            node->buffer->Release();
        }

        node = (AnimationLayer *)node->Next();
    }

    g_lock.Unlock();
}

Int32 GrafixObjectStack::AddAnimation(cBaseAnimation* anime)
{

    if ((m_nNumberOfAnimes + 1) >= MaxNumAnime ) return -1;
    int animationId = m_pAvailableSlot;

    m_pAnime[m_pAvailableSlot] = anime;
    anime->m_nAnimationId = animationId;

    while (m_pAnime[m_pAvailableSlot] != 0) m_pAvailableSlot++;
    m_nNumberOfAnimes++;

    return animationId;
}


Void GrafixObjectStack::InternalDeleteAnime(Int32 index)
{

    if (m_pAnime[index]) {
        delete  m_pAnime[index];
        m_pAnime[index] = 0;
        m_nNumberOfAnimes--;
        if (index < m_pAvailableSlot)
            m_pAvailableSlot = index;
    }

    return;
}
Void GrafixObjectStack::InternalDeleteAllAnime()
{
    int i = 0;
    while (m_nNumberOfAnimes > 0 && i < MaxNumAnime)
    {
        if (m_pAnime[i]) {
            delete  m_pAnime[i];
            m_pAnime[i] = 0;
            m_nNumberOfAnimes--;
        }
        i++;
    }
    return;
}

Void GrafixObjectStack::UpdateAnimation()
{
    int processed = m_nNumberOfAnimes;
    int i=0;
    while (processed > 0 && i < MaxNumAnime) {
        if (m_pAnime[i]) {
            if (m_pAnime[i]->m_bActive) {
                m_bActiveFlag = 1;
                m_pAnime[i]->UpdateAnimation();
            }
            processed--;
        }
        i++;
    }
    return;
}

Void GrafixObjectStack::SetOrientation(
    /* [in] */ ScreenOrientation orientation)
{
    g_lock.Lock();

    Int32 index;
    Void* element;
    ECode ec;

    if (m_orientation == orientation) {
        goto Exit;
    }

#ifdef _neptune
{
    MemoryBuf_<sizeof(ScreenOrientation)> screenOrientation;

    screenOrientation.Copy((Byte *)&orientation, sizeof(ScreenOrientation));

    ec = m_engine->m_currentLayer->m_vga->Control(
            15, screenOrientation, NULL, NULL);
    if (GFX_FAILED(ec)) {
        goto Exit;
    }

    if ((m_orientation % 2) != (orientation % 2)) {
        Swap(m_engine->m_currentLayer->m_info.screen.width,
            m_engine->m_currentLayer->m_info.screen.height);
    }
}
#endif

    if ((m_orientation % 2) != (orientation % 2)) {
        Swap(m_width, m_height);
        ec = m_primarySurface->SetBounds(0, 0, m_width, m_height,
                    (GrafixBound)(GrafixBound_Width | GrafixBound_Height));
        if (GFX_FAILED(ec)) {
            goto Exit;
        }

        GFX_EXPRESSION_TIP(m_primarySurface->Clear(0xFF000000));

        ec = m_backgroundSurface->SetBounds(0, 0, m_width, m_height,
                    (GrafixBound)(GrafixBound_Width | GrafixBound_Height));
        if (GFX_FAILED(ec)) {
            goto Exit;
        }

        GFX_EXPRESSION_TIP(m_backgroundSurface->Clear(0xFF000000));

        ec = m_transitionSurface->SetBounds(0, 0, m_width, m_height,
                    (GrafixBound)(GrafixBound_Width | GrafixBound_Height));
        if (GFX_FAILED(ec)) {
            goto Exit;
        }

        GFX_EXPRESSION_TIP(m_transitionSurface->Clear(0xFF000000));
    }

    m_orientation = orientation;
    m_cursor->m_config.bounds.x = 0;
    m_cursor->m_config.bounds.y = 0;

    fusion_vector_foreach_reverse(element, index, m_windows) {
        CGrafixObject* window = (CGrafixObject *)element;

        window->m_orientationChanged = TRUE;
        window->NotifyEventBuffer(NotifyType_OrientationChanged);

        if (window->Visible()
                && !window->m_surface->m_flipRectangle->IsEmpty()) {
            window->m_needPaint = TRUE;
            window->NotifyEventBuffer(NotifyType_Paint);
        }
    }

    GetFlip();

Exit:
    g_lock.Unlock();
}

ECode TimeWork(
    /* [in] */ Void* pThis)
{
    GrafixObjectStack* stack = (GrafixObjectStack *)pThis;
    IEvent* timeEvent;
    WaitResult wr;
    ECode ec = NOERROR;

    ec = DzCreateEventObj(FALSE, UNSIGNALED, &stack->m_timeEvent);
    if (GFX_FAILED(ec)) {
        return ec;
    }

    timeEvent = stack->m_timeEvent;

    while (TRUE) {
        ec = g_lock.Lock();
        if (GFX_FAILED(ec)) {
            if (!stack->m_terminate) {
                continue;
            }
            else {
                goto Exit;
            }
        }

        if (FusionVectorIsEmpty(&stack->m_timeWindows)) {
            g_lock.Unlock();

            ec = timeEvent->Wait(&wr, NULL);
            if (GFX_FAILED(ec)) {
                goto Exit;
            }

            if (stack->m_terminate) {
                return NOERROR;
            }

            ec = g_lock.Lock();
            if (GFX_FAILED(ec)) {
                if (!stack->m_terminate) {
                    continue;
                }
                else {
                    goto Exit;
                }
            }
        }

        g_lock.Unlock();
        ec = timeEvent->TryWait(TIME_PRECISE, &wr, NULL);
        if (GFX_FAILED(ec)) {
            goto Exit;
        }

        if (stack->m_terminate) {
            return NOERROR;
        }

        ec = g_lock.Lock();
        if (GFX_FAILED(ec)) {
            if (!stack->m_terminate) {
                continue;
            }
            else {
                goto Exit;
            }
        }

        assert(stack);
        TimeHandler(stack);

        g_lock.Unlock();
    }

Exit:
    return ec;
}

ECode TimeHandler(
    /* [in] */ GrafixObjectStack* stack)
{
    Void* pTimeWindow;
    Int32 index;
    UInt32 currentTime;
    ECode ec;

    currentTime = DzTicksToMilliseconds(DzGetElapsedTicks());

    fusion_vector_foreach(pTimeWindow, index, stack->m_timeWindows)
    {
        CGrafixObject   * window = (CGrafixObject *)pTimeWindow;

        ec = window->SetTimeEvent(currentTime);
        if (GFX_FAILED(ec)) {
            return ec;
        }
    }

    stack->m_haveInput = FALSE;

    return NOERROR;
}

ECode ParseTouchEvent(
    /* [in] */ Void* pThis)
{
    MemoryBuf_<sizeof(TouchData)> mbData;
    STATIC Int32 downX = 0;
    STATIC Int32 downY = 0;
    GrafixObjectStack* stack = (GrafixObjectStack *)pThis;
    IDeviceDriver* touch;
    GrafixObjectEvent event;
    TouchData* data = (TouchData *)mbData.GetPayload();
    ECode ec;

    // to mark whether touch is released or not.
    // when animation starts, but touch is not released, this touch event should
    // be sent. otherwise, when animation starts and touch is released, forbid
    // this event.
    //
    Boolean isUp = TRUE;

    ec = DzFindService(L"device:touch", (IInterface **)&stack->m_cursorDriver);
    if (GFX_FAILED(ec)) {
        return ec;
    }

    touch = stack->m_cursorDriver;

    while (TRUE) {
        ec = touch->Read(0, 1, &mbData, NULL);
        if (GFX_FAILED(ec)) {
            return ec;
        }

        if (!stack->m_enableInput && isUp) {
            continue;
        }

        if (stack->m_terminate) {
            return NOERROR;
        }

        isUp = FALSE;

        event.objId = 0;
        event.timestamp = DzTicksToMilliseconds(DzGetElapsedTicks());

        // push
        //
        if (3 == data->dir) {
            downX = data->x;
            downY = data->y;

            event.type = GrafixObjectEventType_StylusHeld;
            event.absoluteX = data->x;
            event.absoluteY = data->y;
        }
        // move
        //
        else if (2 == data->dir) {
            Region region = {downX - 3, downY - 3, downX + 3, downY + 3};

            if (!PointInRegion(&region, data->x, data->y)) {
                downX = data->x;
                downY = data->y;

                event.type = GrafixObjectEventType_StylusMove;
                event.absoluteX = data->x;
                event.absoluteY = data->y;
            }
            else {
                continue;
            }
        }
        // down
        //
        else if (1 == data->dir) {
            downX = data->x;
            downY = data->y;

            event.type = GrafixObjectEventType_StylusPressed;
            event.modifier = InputMask_LButton;
            event.absoluteX = data->x;
            event.absoluteY = data->y;
        }
        // up
        //
        else if (0 == data->dir) {
            isUp = TRUE;

            downX = data->x;
            downY = data->y;

            event.type = GrafixObjectEventType_StylusLifted;
            event.modifier = InputMask_LButton;
            event.absoluteX = data->x;
            event.absoluteY = data->y;
        }
        // push up
        //
        else if (8 == data->dir) {
            isUp = TRUE;

            downX = data->x;
            downY = data->y;

            event.type = GrafixObjectEventType_StylusReleased;
            event.absoluteX = data->x;
            event.absoluteY = data->y;
        }

        if (data->fingers) {
            event.keyValue = event.type;
            event.fingers = data->fingers;
            event.index = data->index;
            event.type = GrafixObjectEventType_MultiTouch;
        }

        ec = g_lock.Lock();
        if (GFX_FAILED(ec)) {
            if (!stack->m_terminate) {
                continue;
            }
            else {
                return ec;
            }
        }

#ifdef _DEBUG
        ec =
#endif
        stack->PostButtonEvent(event);
        g_lock.Unlock();
#ifdef _DEBUG
        if (GFX_FAILED(ec)) {
            // The window maybe detach from the event buffer, so failed
            //
            continue;
        }
#endif
    }

    return NOERROR;
}

ECode ParseKeypadEvent(
    /* [in] */ Void* pThis)
{
    MemoryBuf_<sizeof(KPDData)> mbData;
    GrafixObjectStack* stack = (GrafixObjectStack *)pThis;
    IDeviceDriver* keypad;
    GrafixObjectEvent event;
    KPDData* data = (KPDData *)mbData.GetPayload();
    ECode ec;

    // see the reason in ParseTouchEvent
    //
    Boolean isUp = FALSE;

    ec = DzFindService(L"device:keypad0", (IInterface **)&stack->m_keyDriver);
    if (GFX_FAILED(ec)) {
        return ec;
    }

    keypad = stack->m_keyDriver;

    while (TRUE) {
        ec = keypad->Read(0, 1, &mbData, NULL);
        if (GFX_FAILED(ec)) {
            return ec;
        }

        if (!stack->m_enableInput && isUp) {
            continue;
        }

        if (stack->m_terminate) {
            return NOERROR;
        }

        isUp = FALSE;

        event.objId = 0;
        event.timestamp = DzTicksToMilliseconds(DzGetElapsedTicks());

        // PressOrRelease
        //
        if (data->status & 0x80000000) {
            if (data->status & 0x00000001) {
                event.type = GrafixObjectEventType_KeyHeld;
            }
            else {
                event.type = GrafixObjectEventType_KeyDown;
            }
        }
        else {
            isUp = TRUE;

            if (data->status & 0x00000001) {
                event.type = GrafixObjectEventType_StylusReleased;
            }
            else {
                event.type = GrafixObjectEventType_KeyUp;
            }
        }

        event.keyValue = data->keycode;

        event.modifier = (InputMask)((data->status) & ~0x80000001);

        ec = g_lock.Lock();
        if (GFX_FAILED(ec)) {
            if (!stack->m_terminate) {
                continue;
            }
            else {
                return ec;
            }
        }

#ifdef _DEBUG
        ec =
#endif
        stack->PostKeyEvent(event);
        g_lock.Unlock();
#ifdef _DEBUG
        if (GFX_FAILED(ec)) {
            // The window maybe detach from the event buffer, so failed
            //
            continue;
        }
#endif
    }

    return NOERROR;
}

ECode ParseMouseEvent(
    /* [in] */ Void * pThis)
{
    MemoryBuf_<sizeof(MouseData)> ebbData;
    STATIC Int32 downX = 0;
    STATIC Int32 downY = 0;
    STATIC UInt8 status = 0;
    GrafixObjectStack* stack = (GrafixObjectStack *)pThis;
    IDeviceDriver* mouse;
    GrafixObjectEvent event;
    Region reg = REGION_INIT_FROM_RECTANGLE(
                    &stack->m_primarySurface->m_bornRectangle);
    MouseData* data = (MouseData *)ebbData.GetPayload();
    ECode ec;

    // see the reason in ParseTouchEvent
    //
    Boolean isUp = TRUE;

    ec = DzFindService(L"device:mouse", (IInterface **)&stack->m_cursorDriver);
    if (GFX_FAILED(ec)) {
        return ec;
    }

    mouse = stack->m_cursorDriver;

    while (TRUE) {
        ec = mouse->Read(0, 1, &ebbData, NULL);
        if (GFX_FAILED(ec)) {
            return ec;
        }

        if (!stack->m_enableInput && isUp) {
            continue;
        }

        if (stack->m_terminate) {
            return NOERROR;
        }

        isUp = FALSE;

        event.objId = 0;
        event.timestamp = DzTicksToMilliseconds(DzGetElapsedTicks());

#define MOUSEDOWN_MASK      0x3
#define LEFTBUTTON_MASK     0x1
#define RIGHTBUTTON_MASK    0x2
#define XDECREASE_MASK      0x10
#define YDECREASE_MASK      0x20
#define OVERFLOW_MASK       0xC0

        if ((data->status & MOUSEDOWN_MASK) != (status & MOUSEDOWN_MASK)) {
            if (data->status & MOUSEDOWN_MASK) {
                event.type = GrafixObjectEventType_StylusPressed;

                if (data->status & LEFTBUTTON_MASK) {
                    event.modifier = InputMask_LButton;
                }
                else {
                    event.modifier = InputMask_RButton;
                }
            }
            else {
                isUp = TRUE;

                event.type = GrafixObjectEventType_StylusLifted;

                if (status & LEFTBUTTON_MASK) {
                    event.modifier = InputMask_LButton;
                }
                else {
                    event.modifier = InputMask_RButton;
                }
            }

            event.absoluteX = downX;
            event.absoluteY = downY;

            status = data->status;
        }
        else {
            if (data->status & OVERFLOW_MASK) {
                break;
            }

            if (data->status & XDECREASE_MASK) {
                downX -= (256 - data->x);
            }
            else {
                downX += data->x;
            }

            if (data->status & YDECREASE_MASK) {
                downY += (256 - data->y);
            }
            else {
                downY -= data->y;
            }

            if (downX < reg.left) {
                downX = reg.left;
            }
            else if (downX > reg.right) {
                downX = reg.right;
            }

            if (downY < reg.top) {
                downY = reg.top;
            }
            else if (downY > reg.bottom) {
                downY = reg.bottom;
            }

            event.type = GrafixObjectEventType_StylusMove;
            event.absoluteX = downX;
            event.absoluteY = downY;
        }

        ec = g_lock.Lock();
        if (GFX_FAILED(ec)) {
            if (!stack->m_terminate) {
                continue;
            }
            else {
                return ec;
            }
        }

#ifdef _DEBUG
        ec =
#endif
        stack->PostButtonEvent(event);
        g_lock.Unlock();
#ifdef _DEBUG
        if (GFX_FAILED(ec)) {
            // The window maybe detach from the event buffer, so failed
            //
            continue;
        }
#endif
    }

    return NOERROR;
}

ECode ParseKeyboardEvent(
    /* [in] */ Void * pThis)
{
    MemoryBuf_<sizeof(KBDData)> ebbData;
    GrafixObjectStack* stack = (GrafixObjectStack *)pThis;
    IDeviceDriver* keyboard;
    GrafixObjectEvent event;
    KBDData* data = (KBDData *)ebbData.GetPayload();
    ECode ec;

    // see the reason in ParseTouchEvent
    //
    Boolean isUp = TRUE;

    ec = DzFindService(L"device:keyboard0", (IInterface **)&stack->m_keyDriver);
    if (GFX_FAILED(ec)) {
        return ec;
    }

    keyboard = stack->m_keyDriver;

    while (TRUE) {
        ec = keyboard->Read(0, 1, &ebbData, NULL);
        if (GFX_FAILED(ec)) {
            return ec;
        }

        if (!stack->m_enableInput && isUp) {
            continue;
        }

        if (stack->m_terminate) {
            return NOERROR;
        }

        isUp = FALSE;

        event.objId = 0;
        event.timestamp = DzTicksToMilliseconds(DzGetElapsedTicks());

        // PressOrRelease
        //
        if (data->keyPress) {
            event.type = GrafixObjectEventType_KeyDown;
        }
        else {
            isUp = TRUE;
            event.type = GrafixObjectEventType_KeyUp;
        }

        event.keyValue = data->virKey;

        event.modifier = (InputMask)((data->ledStatus << 16)
                                    || data->kbdStatus);

        ec = g_lock.Lock();
        if (GFX_FAILED(ec)) {
            if (!stack->m_terminate) {
                continue;
            }
            else {
                return ec;
            }
        }

#ifdef _DEBUG
        ec =
#endif
        stack->PostKeyEvent(event);
        g_lock.Unlock();
#ifdef _DEBUG
        if (GFX_FAILED(ec)) {
            // The window maybe detach from the event buffer, so failed
            //
            continue;
        }
#endif
    }

    return NOERROR;
}
